import { isPlainObject, reduce } from 'lodash-es'
import {
  Action,
  getModule,
  Module,
  Mutation,
  VuexModule
} from 'vuex-module-decorators'

import { getDictionaryList, getMyAppList } from '@/api/sys/app'
import type { AppInfo, AppList, Dict } from '@/api/sys/model/appModel'
import { useGlobalSetting } from '@/settings/system'
import { hasCacheA } from '@/store/plugins'
import { getAppInfo, getDictByCode, getDictMappingByCode } from '@/utils'

import store from '../instance'
import { userStore } from './user'

const { tenantId, isCustom } = useGlobalSetting()

interface AppState {
  readonly appList: MaybeNull<AppList>

  readonly currentAppInfo: MaybeNull<AppInfo>

  readonly dictionary: MaybeNull<Record<string, Dict>>

  readonly tenantId: string
}

@Module({
  namespaced: true,
  name: 'app',
  dynamic: true,
  store,
  preserveState: hasCacheA('app')
})
class App extends VuexModule implements AppState {
  private _appList: MaybeNull<AppList> = null

  private _currentAppInfo: MaybeNull<AppInfo> = null

  private _dictionary: MaybeNull<Record<string, Dict>> = null

  /* 选择的租户Id */
  private _selectTenantId = ''
  /* 外部带进来的租户Id（扫码，链接等） */
  private _externalTenantId = ''
  /* 根据定位自动配置的租户Id */
  private _locationTenantId = ''

  private _lastUpdateTime = 0

  get appList() {
    return this._appList
  }

  get currentAppInfo() {
    return this._currentAppInfo
  }

  get dictionary() {
    return this._dictionary
  }

  get tenantId() {
    /*
     * 租户Id获取
     * 优先级：定制 > 选择 > 登录 > 外部传入 > 定位 > 默认租户
     * 针对不同小程序，登录获取的租户Id不一定是账户的租户，可在 userStore 更改输出
     * 为用户信息内的租户
     */
    return (
      (isCustom && tenantId) ||
      this._selectTenantId ||
      userStore.getLoginTenantId ||
      this._externalTenantId ||
      this._locationTenantId ||
      tenantId
    )
  }

  get initDefaultTenant() {
    return (
      this._selectTenantId ||
      userStore.getLoginTenantId ||
      this._locationTenantId
    )
  }

  get locationTenantId() {
    return this._locationTenantId || this._selectTenantId
  }

  @Mutation
  setAppList(val: AppList): void {
    this._appList = val
  }

  @Mutation
  setDict(val: Record<string, Dict>): void {
    this._dictionary = val
  }

  @Mutation
  setSelectTenant(val: string): void {
    this._selectTenantId = val
  }

  @Mutation
  setExternalTenant(val: string): void {
    this._externalTenantId = val
  }

  @Mutation
  setLocationTenant(val: string): void {
    this._locationTenantId = val
  }

  @Mutation
  setLastUpdateTime(): void {
    this._lastUpdateTime = Date.now()
  }

  @Mutation
  $resetState() {
    this._appList = null
    this._dictionary = null
    this._locationTenantId = ''
    this._selectTenantId = ''
    this._externalTenantId = ''
    this._lastUpdateTime = 0
  }

  @Mutation
  setCurrentAppInfo(val: string | string[] | AppInfo) {
    if (isPlainObject(val)) {
      this._currentAppInfo = val as AppInfo
    } else {
      this._currentAppInfo = getAppInfo(val as string | string[]) ?? null
    }
  }

  @Action
  async getDictByCode(code: string, type: 'original' | 'option' = 'option') {
    return getDictByCode(code, type as 'option')
  }

  @Action
  async getDictMappingByCode(
    code: string,
    findVal?: string
  ): Promise<string | AnyObject> {
    return getDictMappingByCode(code, findVal)
  }

  @Action
  async requestMyAppList() {
    const { appCode } = useGlobalSetting()
    const result = await getMyAppList({ appCode })
    this.setAppList([result])
    return [result]
  }

  @Action
  async requestDict() {
    const result = await getDictionaryList()
    const transformData = reduce(
      result,
      (pre, { code, children }) => {
        pre[code] = children
        return pre
      },
      {} as AnyObject
    )
    this.setDict(transformData)
    return transformData
  }
}

export const appStore = getModule<App>(App)
